package com.apobates.forum.orize.core;

import com.apobates.forum.orize.Orize;
import com.apobates.forum.orize.OrizeAction;
import com.apobates.forum.orize.OrizeResource;
import com.apobates.forum.orize.OrizeResources;
import com.apobates.forum.orize.core.plug.OrizeResourceXmlLoader;
import org.reflections.Reflections;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 扫描Orize注解并生成资源文件
 * 注意:
 * 不提供具体的Path信息，不同的前端框架可能提供不同的注解来配置Path
 * @author xiaofanku@live.cn
 * @since 20210822
 */
public abstract class OrizeAnnotationScanner {
    /**
     * 合并两个Path, 第一个参数为: 类上的映射地址, 第二个参数为: 方法上的映射地址
     */
    protected final static BiFunction<String,String,String> mergePathFun = (classPath, methodPath)->{
        return classPath + ((!methodPath.startsWith("/"))?"/":"") + methodPath;
    };
    private final static String SPLIT_S="::";
    private final static Logger logger = LoggerFactory.getLogger(OrizeAnnotationScanner.class);

    /**
     * 扫描Orize注解
     *
     * @param basePackageName 需要扫描的包全名
     * @return
     */
    public List<OrizeResource> scan(String basePackageName){
        Reflections reflections = new Reflections(new ConfigurationBuilder()
                .setUrls(ClasspathHelper.forPackage(basePackageName))
                .setScanners(new MethodAnnotationsScanner()));
        Set<Method> methods = reflections.getMethodsAnnotatedWith(Orize.class);
        return methods.stream().map(this::extractResource).flatMap(Function.identity()).collect(Collectors.toList());
    }

    /**
     * 扫描Orize注解并生成资源文件
     *
     * @param basePackageName 需要扫描的包全名
     * @param webInfPath
     * @return
     */
    public String scanAndSave(String basePackageName, String webInfPath){
        List<OrizeResource> rs = scan(basePackageName);
        OrizeResources rss = new OrizeResources(System.currentTimeMillis()+"", rs);
        try {
            OrizeResourceXmlLoader.marshal(rss, webInfPath + ""+OrizeResourceXmlLoader.ANNO_RES_DEF_FILENAME);
            return OrizeResourceXmlLoader.ANNO_RES_DEF_FILENAME;
        }catch (Exception e){
            if(logger.isDebugEnabled()){
                logger.debug(e.getMessage(), e);
            }
        }
        throw new IllegalStateException("operate break");
    }

    /**
     * 从反射的方法中提取OrizeResource
     * 注意: 需要子类提供parsePath方法的实现
     *
     * @param method
     * @return
     */
    private Stream<OrizeResource> extractResource(Method method){
        final Orize anno = method.getAnnotation(Orize.class);
        //拼接后的资源地址(A)(尾)
        final List<String> pathList = parsePath(method);
        //可能的多个动作(B)(头)
        List<String> actionList = Stream.of(anno.action()).map(OrizeAction::name).collect(Collectors.toList());

        BiFunction<String,String,String> joinActionPathFun = (reqAction, reqPath)->{
            return reqAction + SPLIT_S + reqPath;
        };
        //拼接:A+B = Ele
        List<String> crossjoinResult = cartesian(pathList, actionList, joinActionPathFun);

        return crossjoinResult.stream().map(rp->{
            // action = rp(0, "::");
            int si = rp.indexOf(SPLIT_S);
            String _tmpAction = rp.substring(0, si);
            // request path = rp("::", length)
            String _tmpReqPath = rp.substring(si+2);
            return new OrizeResource(
                    _tmpReqPath,
                    anno.method(),
                    String.join(",", anno.roles()),
                    anno.spot(),
                    _tmpAction,
                    anno.slot(),
                    anno.slotKeys());
        });
    }

    /**
     * 具体的前端框架实行从Method中解析出OrizeResource.path
     * @param method
     * @return
     */
    protected abstract List<String> parsePath(Method method);

    /**
     * 使用Stream来生成笛卡尔集
     *
     * @param tailList 尾部集合
     * @param headList 头部集合
     * @param mergePathFun 合并的映射函数(第一个参数:头,第二个参数:尾)
     * @return
     */
    protected static List<String> cartesian(List<String> tailList, List<String> headList, BiFunction<String,String,String> mergePathFun){
        return headList.stream().flatMap(pre->tailList.stream().flatMap(tail->{
            String _tmp = mergePathFun.apply(pre, tail);
            return Stream.of(_tmp);
        })).collect(Collectors.toList());
    }

    /**
     * 使用临时列表来生成笛卡尔集
     *
     * @param tailList 尾部集合
     * @param headList 头部集合
     * @param mergePathFun 合并的映射函数(第一个参数:头,第二个参数:尾)
     * @return
     */
    protected static List<String> cartesianPro(String[] tailList, String[] headList, BiFunction<String,String,String> mergePathFun){
        List<String> cartesianProduct = new ArrayList<>();
        Arrays.asList(headList).forEach(pre -> Arrays.asList(tailList).forEach(tail -> {
            String _tmp = mergePathFun.apply(pre, tail);
            cartesianProduct.add(_tmp);
        }));
        return cartesianProduct;
    }
}
